package smallfrench.moteur.biblio;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import smallfrench.moteur.biblio.err.ErreurIndex;
import smallfrench.moteur.biblio.err.ErreurParametres;
import smallfrench.moteur.interp.RObjet;
import smallfrench.moteur.interp.err.RException;

/**
 * Représente un dictionnaire.
 * @author antoine1023
 */
public class ADict extends AIterable {

	public static final AClasse CLASSE = AClasse.creer(ADict.class, "Dict");

	public final HashMap<RChaine, RObjet> map;

	/**
	 * 
	 * @param parametres
	 */
	@AnnotationMembre(parametresVariables = true)
	public ADict(RObjet[] parametres) {

		map = new HashMap<RChaine, RObjet>();
		int nParams = parametres.length;

		if (nParams % 2 != 0) {
			new ErreurParametres(
					"Le nombre de paramètres doit être pair").lancer();
		}

		for (int i = 0; i < nParams; i+=2) {

			RObjet objClee = parametres[i];
			if (! (objClee instanceof RChaine))
				new ErreurParametres(
						"Le paramètre " + i + " doit être une Chaine").lancer();

			RObjet valeur = parametres[i + 1];

			map.put((RChaine) objClee, valeur);
		}
	}


	@AnnotationMembre
	public RBooleen contientClee(RChaine clee) {
		return RBooleen.nouveau(map.containsKey(clee));
	}


	@Override
	public RChaine enChaine() {
		StringBuilder sb = new StringBuilder("Dict(");
		boolean premier = true;
		for (Map.Entry<RChaine, RObjet> e : map.entrySet()) {
			if (premier) {
				premier = false;
			} else {
				sb.append(" ; ");
			}
			sb.append(e.getKey());
			sb.append(' ');
			sb.append(e.getValue());
		}
		sb.append(')');
		return new RChaine(sb.toString());
	}


	@Override
	@AnnotationMembre
	public AIterateur iterateur() {
		return new AIterateurDict(map);
	}


	@AnnotationMembre
	public RObjet obtenir(RChaine clee) {
		RObjet v = map.get(clee);
		if (v == null)
			throw ErreurIndex.nouvelleException(clee);
		return v;
	}


	@Override
	public RObjet operateurAssignationIndexBase(RObjet index, RObjet autre) {
		if (index instanceof RChaine) {
			RChaine clee = (RChaine) index;
			map.put(clee, autre);
			return RRien.RIEN;
		}
		return null;
	}


	@Override
	public RObjet operateurEgalBase(RObjet autre) throws RException {
		if (autre instanceof ADict) {
			ADict d = (ADict) autre;
			return RBooleen.nouveau(d.map.equals(map));
		}
		return null;
	}


	@Override
	public RObjet operateurIndexBase(RObjet autre) throws RException {
		if (autre instanceof RChaine)
			return obtenir((RChaine) autre);
		return null;
	}


	@Override
	public RObjet operateurInegalBase(RObjet autre) throws RException {
		if (autre instanceof ADict) {
			ADict d = (ADict) autre;
			return RBooleen.nouveau(! d.map.equals(map));
		}
		return null;
	}


	public static class AIterateurDict extends AIterateur {

		public static final AClasse CLASSE = AClasse.creer(
				AIterateurDict.class, "IterateurDict");

		private final Iterator<Map.Entry<RChaine, RObjet>> iter;

		@AnnotationMembre
		public AIterateurDict(HashMap<RChaine, RObjet> map) {
			this.iter = map.entrySet().iterator();
		}

		@Override
		@AnnotationMembre
		public RBooleen contientAutres() {
			return RBooleen.nouveau(iter.hasNext());
		}

		/**
		 * Retourne un tuple (clee ; valeur)
		 */
		@Override
		@AnnotationMembre
		public RObjet suivant() {
			if (! iter.hasNext())
				return RRien.RIEN;

			Map.Entry<RChaine, RObjet> e = iter.next();
			return new ATuple(new RObjet[] {e.getKey(), e.getValue()});
		}
	}
}
